package au.edu.swin.MelbourneMap;

import java.io.*;
import java.util.ArrayList;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.AbstractButton;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import java.awt.geom.*;

/**
 * Implementation of Controller class
 * Using Model View Controller architecture.
 *
 * @author  Alexis Wellavize (s/n: 6033482)<br/>
 *           Dotan Tamir (s/n: 6532969)
 * @since   v1, created:  17/09/2012
 * @version v2, modified:  22/09/2012
 *
 */
 
public class Controller
{
    // The view object the controller has access to update
    private View view;
    // The model object the controller has access to update
    private Model model;
    // The filename from open and save operations
    private String file;
    // The counter to change transparancy of labels based on zoom factor
    private int counter;
    // The amount of panning left, right, up and down
    private double panningFactor;
    
    
    /**
     * Overloaded constructor
     * @param   view - View instance for controller to communicate with.
     * @param   model - Model instance for controller to communicate with.
     */
    public Controller( View view, Model model )
    {
        this.view = view;
        this.model = model;
        counter = 0;                        // Initial value
        panningFactor = 10000.0;            // Default value
        
        // Register event listerners to GUI
        view.addButtonActionListeners(
            new ButtonActionListener( this ) );
        view.addMenuActionListeners(
            new MenuActionListener( this ) );
        view.getDesktop().addMouseMotionListener(
            new MouseMotionActionListener( this ) );
        view.getDesktop().addMouseListener(
            new MouseActionListener( this ) );
    }
    
    // Button click event handler
    class ButtonActionListener
        implements ActionListener
    {

        private Controller controller;
        
        /**
         * Overloaded constructor
         * @param   controller - Controller instance
         */
        ButtonActionListener( Controller controller )
        {
            this.controller = controller;
        }
        
        /**
         * Action to perform on button click events
         * @param e - ActionEvent object
         */
        public void actionPerformed( ActionEvent e )
        {
            String name = e.getActionCommand().toLowerCase();

            if( name.equals( "chosen" ) )
            {
                // TODO - Replace drop down with select regions by mouse click
                //if ( model.isChangeRegionColorEnabled() )
                //{
                    JComboBox cb = (JComboBox)e.getSource();
                    String suburbName = (String)cb.getSelectedItem();
                    Color oldBackgroundColor = Color.GREEN;
                    Color newBackgroundColor =
                        JColorChooser.showDialog(
                            null,
                            "Change Suburb Colour",
                            oldBackgroundColor );
                    
                    if ( newBackgroundColor != null ) 
                    {
                        for ( Region r : model.getRegions().values() )
                        {
                            if( r.getName().equals( suburbName ) )
                                r.setColor( newBackgroundColor );
                        }
                    }
                //}
            }
            
            // Actions for button clicks
            if ( name.equals( "^" ) )
            {
                model.setTranslation( 0.0, -panningFactor );
                model.setRectangle(
                    getPathRect(
                        0, 0, model.getWidth(), model.getHeight() ) );
            }

            if ( name.equals( ">" ) )
            {
                model.setTranslation( -panningFactor, 0.0 );
                model.setRectangle(
                    getPathRect(
                        0,0, model.getWidth(), model.getHeight() ) );
            }

            if ( name.equals( "v" ) )
            {
                model.setTranslation( 0.0, panningFactor );
                model.setRectangle(
                    getPathRect(
                        0, 0, model.getWidth(), model.getHeight() ) );
            }

            if ( name.equals( "<" ) )
            {
                model.setTranslation( panningFactor, 0.0 );
                model.setRectangle(
                    getPathRect(
                        0, 0, model.getWidth(), model.getHeight() ) );
            }

            if ( name.equals( "reset" ) )
            {
                model.resetScale();
                counter =0;
                panningFactor = 10000;
                model.setTransparency( counter );
            }

            if ( name.equals( "+" ) )
            {
            	model.setRoadDataChanged( true );   //TODO: Only for testing porpuses
            	model.setScale( 1.1 );
            	counter++;
            	model.setTransparency( counter );
                
            	if( panningFactor > 5000 )
            		panningFactor -=1000;
            	else
            		panningFactor = 1000;
            	
            	model.setRectangle(
                    getPathRect(
                        0, 0,
                        (int)(model.getWidth()*0.9),
                        (int)(model.getHeight()*0.9) ) );
            }

            if ( name.equals("-") )
            {
                model.setScale( 0.9 );
                counter--;
            	model.setTransparency( counter );
                
            	if( panningFactor < 90000 )
            		panningFactor +=1000;
            	else
            		panningFactor = 90000;
            	
            	model.setRectangle(
                    getPathRect(
                        0, 0,
                        (int)(model.getWidth()*1.1),
                        (int)(model.getHeight()*1.1) ) );
            }

            // Repaint GUI
            view.redraw();
        }
    }

    // Menu click event handler    
    class MenuActionListener
        implements ActionListener
    {

        private Controller controller;
        
        /**
         * Overloaded constructor
         * @param   controller - Controller instance
         */        
        MenuActionListener( Controller controller )
        {
            this.controller = controller;
        }

        /**
         * Action to perform on button click events
         * @param e - ActionEvent object
         */        
        public void actionPerformed ( ActionEvent e )
        {
            String name = e.getActionCommand().toLowerCase();
            
            // Actions for menu clicks
            try
            {
                // File -> Save As
            	if ( name.equals( "road data" ) )
            	{
                    file = view.showSaveAsFileDialog();
                    if ( file != null )
                        model.saveRoadData( file );
            	}
                else if ( name.equals( "rail data" ) )
                {
                    // TODO Implement ...
                    //file = view.showSaveAsFileDialog();
                    //if ( file != null )
                    //    model.saveRailData( file );                
                }
                else if ( name.equals( "region data" ) )
                {
                    // TODO Implement ...
                    //file = view.showSaveAsFileDialog();
                    //if ( file != null )
                    //    model.saveReigionData( file );
                }

                // File -> Save
            	if( name.equals( "save" ) )
            	{	
                    if ( model.isRoadDataChanged() )
                    {
            			model.saveRoadData();
                    }
                    else if ( model.isRailDataChanged() )
                    {
                        // TODO: Implement ...
                        //model.saveRailData();
                    }
                    else if ( model.isRegionDataChanged() )
                    {
                        // TODO: Implement ...                    
                        //model.saveRegionData();
                    }
            	}
                
                // File -> Open
                if ( name.equals( "open" ) )
                {
                    file = view.showOpenFileDialog();
                    
                    if ( file != null )
                    {
                        model.readScenarioFile( file );
                        view.populateJComoboBox();
                    }
                }
            }
            catch ( IOException io )
            {
                System.out.println( "ERR: Cannot read file - " + io.toString() );
            }
            
            // File -> Exit
            if ( name.equals( "exit" ) )
            {
                System.exit(0);
            }
                
            // Help -> About    
            if ( name.equals( "about" ) )
                view.showAboutDialog();
            
            // View -> Show Labels
            if ( name.equals( "show labels" ) )
            {
                AbstractButton aButton = (AbstractButton) e.getSource();
                boolean selected = aButton.getModel().isSelected();
                
                if ( selected )
                    model.setLabelVisibility( true );
                else
                    model.setLabelVisibility( false );
            } 
            
            // Edit -> Rename Road
            if ( name.equals( "rename road" ) )
            {
            
                System.out.println( " here " );
                AbstractButton aButton = (AbstractButton) e.getSource();
                boolean selected = aButton.getModel().isSelected();
                
                if ( selected )
                    model.enableRenameRoad( true );
                else
                    model.enableRenameRoad( false );
            }
            
            /*
            // Edit -> Change Suburb Colour
            if ( name.equals( "change suburb colour" ) )
            {
                AbstractButton aButton = (AbstractButton) e.getSource();
                boolean selected = aButton.getModel().isSelected();
                
                if ( selected )
                    model.enableChangeRegionColor( true );
                else
                    model.enableChangeRegionColor( false );
            }
            */
            
            // View -> Show Shortest Path
            if ( name.equals( "show shortest path" ) )
            {
                AbstractButton aButton = (AbstractButton) e.getSource();
                boolean selected = aButton.getModel().isSelected();
                
                if ( selected )
                    model.enableShowShortestPath( true );
                else
                    model.enableShowShortestPath( false );
            } 

            // View -> Show Zoom Panel
            if ( name.equals( "show zoom panel" ) )
				try {
					view.createFrame();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (NoninvertibleTransformException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
            
            // Repaint GUI
            view.redraw();
        }
    }
    	
    // Mouse motion event handler        
    class MouseMotionActionListener
        implements MouseMotionListener
    {

        private Controller controller;

        /**
         * Overloaded constructor
         * @param   controller - Controller instance
         */         
        MouseMotionActionListener( Controller controller )
        {
            this.controller = controller;
        }
       
        /**
         * Action to perform on mouse move events
         * @param e - MouseEvent object
         */        
        @Override
        public void mouseMoved( MouseEvent e )
        {
        	if(view.getMapPanel().getMapTransform().getDeterminant() != 0.0)
        	{
	        	Point2D point = new Point2D.Double(e.getX(), e.getY());
	        	try {
	            	point = view.getMapPanel().getMapTransform().inverseTransform( point, null );
					view.setStatusLabel(
					    "File loaded: " + model.getScenarioFileName() + " - " +
					    "POS [X,Y]: " + (int)point.getX()+" , "+(int)point.getY()    );
					view.setTitle("File loaded: " + model.getScenarioFileName() + " - " +
					    "POS [X,Y]: " + (int)point.getX()+" , "+(int)point.getY());
				} catch (NoninvertibleTransformException e1) {
					
					System.out.println("Err " + e1.getMessage());
				}
        	}
        }

        /**
         * Action to perform on mouse drag events
         * @param e - MouseEvent object
         */
        @Override
        public void mouseDragged( MouseEvent e )
        {
            // TODO Implement ...
        }
    }

    // Mouse click event handler        
    class MouseActionListener
        implements MouseListener
    {

        private boolean secondClick = false;
        private Point2D p1;
        private Point2D p2;
        private Controller controller;
        
        /**
         * Overloaded constructor
         * @param   controller - Controller instance
         */        
        MouseActionListener( Controller controller )
        {
            this.controller = controller;
        }
        
        /**
         * Action to perform on mouse button released events
         * @param e - MouseEvent object
         */         
        @Override
        public void mouseReleased( MouseEvent e )
        {
            // TODO implement ...
        }

        /**
         * Action to perform on mouse button pressed events
         * @param e - MouseEvent object
         */          
        @Override
        public void mousePressed( MouseEvent e )
        {
            if ( model.isRenameRoadEnabled() )
            {
                Road r = null;
                String str ="";
                try
                {
                    Point2D p1 = new Point2D.Double( e.getX(), e.getY() );
                    r = model.selectRoad( p1, view.getMapPanel().getMapTransform() );

                    if( !r.equals( null ) )
                    {	

                        r.setIsSelected( !r.getIsSelected() );
                        view.redraw();
                        
                        str =
                            (String)JOptionPane.showInputDialog(
                            null,
                            "Enter new name : ",
                            "Rename road",
                            1,
                            null,
                            null,
                            r.getName() );
                            
                        r.setIsSelected( !r.getIsSelected() );
                        view.redraw();
                    }
                      
                    if( str != null )
                    {
                        JOptionPane.showMessageDialog(
                            null,
                            "Road renamed to : " + str,
                            "Road renamed",
                            1 );
                         
                        r.setName( str );
                    }
                }
                catch ( Exception ex )
                {
                    System.out.println( "ERR: Error in mouse press - " + ex.getMessage() );
                }
            }
            
            if ( model.isShowShortestPathEnabled() )
            {
                try
                { 
                    if ( secondClick )
                    {
                        p2 = new Point2D.Double( e.getX(), e.getY() );
                        System.out.println( "second point clicked: " + p2.getX() + ", " + p2.getY() );                  
                        Path2D p = model.getShortestPath( p1, p2, view.getMapPanel().getMapTransform() );
                        view.getMapPanel().drawShortestPath( p );
                        secondClick = false;
                    }
                    else
                    {
                        p1 = new Point2D.Double( e.getX(), e.getY() );
                        System.out.println( "first point clicked: " + p1.getX() + ", " + p1.getY() );
                        secondClick = true;
                    }
                }
                catch ( Exception ex )
                {
                    System.out.println( "ERR: Error in mouse press - " + ex.getMessage() );                
                }
            }
            
            /*
            if ( model.isChangeRegionColorEnabled() )
            {
                try
                {    
                    System.out.println ( " here " );
                    
                    Point2D p1 = new Point2D.Double( e.getX(), e.getY() );
                    Region r = model.selectRegion( p1, view.getMapPanel().getMapTransform() );                
                
                    Color newColor =
                        JColorChooser.showDialog(
                            null,
                            "Choose a new suburb colour",
                             r.getColor() );
                        
                    if ( newColor != null ) 
                        r.setColor( newColor );
                }
                catch ( Exception ex )
                {
                    System.out.println( "ERR: Error in mouse press - " + ex.getMessage() );
                }                        
            }
            */
        }

        /**
         * Action to perform on mouse exited events
         * @param e - MouseEvent object
         */  
        @Override
        public void mouseExited( MouseEvent e )
        {
            // TODO Implement ...
        }

        /**
         * Action to perform on mouse entered events
         * @param e - MouseEvent object
         */          
        @Override
        public void mouseEntered( MouseEvent e )
        {
            // TODO Implement ...
        }

        /**
         * Action to perform on mouse clicked events
         * @param e - MouseEvent object
         */          
        @Override
        public void mouseClicked( MouseEvent e )
        {
            // TODO Auto-generated method stub
        }
    }
    
    /**
     * Gets the zoom rectangle
     * @return Path2D - Rectangle for zoom panel
     */    
    private Path2D getPathRect( int x, int y, int w, int h )
    {
    	ArrayList<Point2D> rect = new ArrayList<Point2D>();
    	Path2D path = new Path2D.Double();
        Point2D topLeft = new Point2D.Double( x, y );
        Point2D topRight = new Point2D.Double( w, x );
        Point2D bottomLeft = new Point2D.Double( x, h );
        Point2D bottomRight = new Point2D.Double( w, h );
        double x1, y1;
        
        try
        {
	        rect.add( view.getMapPanel().getMapTransform().inverseTransform( topLeft, null ));
	        rect.add( view.getMapPanel().getMapTransform().inverseTransform( topRight, null ));
	        rect.add( view.getMapPanel().getMapTransform().inverseTransform( bottomRight, null ));
	        rect.add( view.getMapPanel().getMapTransform().inverseTransform( bottomLeft, null ));
	    	
	    	for( int i = 0; i < rect.size(); i++ )
	    	{
	    		x1 = (rect.get(i).getX());
	    		y1 = (rect.get(i).getY());
	    		
	    		if( i == 0 )
	    			path.moveTo( x1, y1 );
	    		else
	    			path.lineTo( x1, y1 );
	    	}
            
	    	path.closePath();
	    	return path;
        }
        catch( Exception ex )
        {
            System.out.println( "ERR: In getPathRect - " + ex.getMessage()  );
        }
    	
    	return null;
    }
}
